﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GoogleContactMerge
{
    public partial class _mainForm : Form
    {
        ContactMerge _merge;

        public _mainForm()
        {
            InitializeComponent();
            
            this._detailGrid.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
            this._gridList.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
            this._gridList.SelectionMode = DataGridViewSelectionMode.CellSelect;
            this._gridList.AllowUserToOrderColumns = true;
            this._detailGrid.SelectionMode = DataGridViewSelectionMode.CellSelect;
            this._detailGrid.AllowUserToOrderColumns = true;

            this._detailGrid.DataBindingComplete += new DataGridViewBindingCompleteEventHandler(_detailGrid_DataBindingComplete);
            this._gridList.DataBindingComplete += new DataGridViewBindingCompleteEventHandler(_gridList_DataBindingComplete);
        }

        void _gridList_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            foreach (DataGridViewColumn column in this._gridList.Columns)
            {
                column.Visible = false;
                if (column.Name == "Name")
                {
                    column.Visible = true;
                    column.ReadOnly = true;
                    column.SortMode = DataGridViewColumnSortMode.Automatic;
                }
                else if (column.Name == "EmailOther")
                {
                    column.Visible = true;
                    column.ReadOnly = true;
                    column.SortMode = DataGridViewColumnSortMode.Automatic;
                }
                else if (column.Name == "Merge")
                {
                    column.Visible = true;
                    column.HeaderText = "Include in Merge?";
                }
            }
        }

        void _detailGrid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            foreach (DataGridViewColumn column in this._detailGrid.Columns)
            {
                column.Visible = false;
                if (column.Name == "Name")
                {
                    column.Visible = true;
                    column.DisplayIndex = 1;
                }
                if (column.Name == "Delete")
                {
                    column.Visible = true;
                    column.DisplayIndex = 0;
                }
                if (column.Name == "EmailOther")
                {
                    column.Visible = true;
                    column.DisplayIndex = 2;
                }
                if (column.Name.Contains("Email"))
                    column.Visible = true;
                if (column.Name.Contains("Phone"))
                    column.Visible = true;
            }

            this._detailGrid.Columns["Delete"].Frozen = true;
        }

        static DetailModel GetModel(DataGridViewRow row)
        {
            return row.DataBoundItem as DetailModel;
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this._splitterDistanceExpanded = this._split.SplitterDistance;
            this.ContextMenuStrip = new ContextMenuStrip();
            this.ContextMenuStrip.Opening += this.ContextMenuStrip_Opening;
        }

        private void _loginButton_Click(object sender, EventArgs e)
        {
            using (LoginForm form = new LoginForm())
            {
                form.StartPosition = FormStartPosition.CenterParent;
                if (DialogResult.OK == form.ShowDialog(this))
                {
                    this._merge = new ContactMerge(form.EmailAddress, form.Password);
                    var ex = this._merge.Login();
                    if (ex != null)
                    {
                        MessageBox.Show(this, ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }
                    else
                    {
                        this.DownloadContacts();
                    }
                }
            }
        }

        private void _quitButton_Click(object sender, EventArgs e)
        {
            var exit = MessageBox.Show("Exit and discard changes?", Application.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
            if(exit == DialogResult.OK)
                Application.Exit();
        }

        List<DetailModel> _currentMergeSet = new List<DetailModel>();
        private void _downloadContactsButton_Click(object sender, EventArgs e)
        {
            DownloadContacts();
        }

        private void DownloadContacts()
        {
            //clear out the current merge set
            this._currentMergeSet.Clear();
            this._filterTextBox.Text = string.Empty;
            this._searchTextBox.Text = string.Empty;

            var models = this._merge.GetDetailModels();
            this._gridList.DataSource = models;
            this._detailGrid.DataSource = new BindingList<DetailModel>(this._currentMergeSet);
        }

        void GetMergeValues(Int32 rowIndex, Int32 columnIndex)
        {
            var currentCellValue = this._detailGrid.Rows[rowIndex].Cells[columnIndex].Value;
  
            foreach (DataGridViewRow row in this._detailGrid.Rows)
            {
                var value = row.Cells[columnIndex].Value as string;
                var rowInMerge = (bool)row.Cells["Merge"].Value == true;
                var currentCellValueString = currentCellValue as string;
                if (rowInMerge
                    && value != null
                    && (currentCellValueString == null || currentCellValueString != value)
                    && !ItemInMenu(this.ContextMenuStrip, value))
                {
                    var donorName = row.Cells["Name"].Value ?? row.Cells["EmailPrimary"].Value ?? "<Missing>";
                    const string menuItemTextBase = "     {0} (from '{1}')";
                    var menuItemText = string.Format(menuItemTextBase, value, donorName);
                    this.ContextMenuStrip.Items.Add(menuItemText, null, this.SuggestedValueClicked).Tag = value;
                }
            }
        }

        private void _detailGrid_CellEnter(object sender, DataGridViewCellEventArgs e)
        {

        }

        /*                ToolStripItem menuItem;

                menuItem = this.ContextMenuStrip.Items.Add(
                    string.IsNullOrEmpty(workEmail) ?
                    string.Format(copyToEmail, "Other", "Work") :
                    string.Format(copyToEmailOverwrite, "Other", "Work", workEmail));
                menuItem.Tag = new KeyValuePair<string, string>(otherEmail as string, "EmailWork");
                menuItem.Click += CopyToMenuItemClick;

                menuItem = this.ContextMenuStrip.Items.Add(
                    string.IsNullOrEmpty(homeEmail) ?
                    string.Format(copyToEmail, "Other", "Home") :
                    string.Format(copyToEmailOverwrite, "Other", "Home", homeEmail));
                menuItem.Tag = new KeyValuePair<string, string>(homeEmail as string, "EmailHome");
                menuItem.Click += CopyToMenuItemClick;


                if (workEmail != null)
                {
                    menuItem = this.ContextMenuStrip.Items.Add(
                        string.IsNullOrEmpty(workEmail) ?
                        replaceWithWorkEmail :
                        string.Format(replaceWithWorkEmailOverwrite, workEmail));
                    menuItem.ToolTipText = "";
                    menuItem.Tag = new KeyValuePair<string, string>(workEmail, "EmailOther");
                    menuItem.Click += ReplaceValueMenuItemClick;
                }
                if (homeEmail != null)
                {
                    menuItem = this.ContextMenuStrip.Items.Add(
                        string.IsNullOrEmpty(homeEmail) ?
                        replaceWithHomeEmail :
                        string.Format(replaceWithHomeEmailOverwrite, homeEmail));
                    menuItem.Tag = new KeyValuePair<string, string>(homeEmail, "EmailOther");
                    menuItem.Click += ReplaceValueMenuItemClick;
                }

                this.ContextMenuStrip.Items.Add("-");
                this.ContextMenuStrip.Items.Add(new ToolStripLabel(string.Format("Overwrite {0} Email with...","Home")));
                this.GetMergeValues(rowIndex, columnIndex);

                this.ContextMenuStrip.Items.Add("-");
                AddClipboardMenuItems(currentCell, cut, copy, paste, delete);
*/

        void AddCellMenuItems(
            string fieldGroupName,
            DataGridViewCell currentCell,
            string currentFieldName, 
            string currentPropertyName, 
            string altFieldName1, 
            string altPropertyName1,
            string altFieldName2,
            string altPropertyName2)
        {
            string replaceWith = "     Replace with {0} " + fieldGroupName + " ('{1}')";
            string replaceWithOverwrite = "     Replace with {0} "+ fieldGroupName +" ('{1}', overwrite '{2}')";

            string copyTo = "     Copy '{0}' to {1} " + fieldGroupName;
            string copyToOverwrite = "     Copy '{0}' to {1} " + fieldGroupName +" (overwrite '{2}')";

            var currentFieldValue = currentCell.OwningRow.Cells[currentPropertyName].Value as string;
            var altFieldValue1 = currentCell.OwningRow.Cells[altPropertyName1].Value as string;
            var altFieldValue2 = currentCell.OwningRow.Cells[altPropertyName2].Value as string;

            ToolStripItem menuItem;

            if (currentFieldValue != altFieldValue1)
            {
                menuItem = this.ContextMenuStrip.Items.Add(
                    string.IsNullOrEmpty(altFieldValue1) ?
                    string.Format(copyTo, currentFieldValue, altFieldName1) :
                    string.Format(copyToOverwrite, currentFieldValue, altFieldName1, altFieldValue1));
                menuItem.Tag = new KeyValuePair<string, string>(currentFieldValue, altPropertyName1);
                menuItem.Click += CopyToMenuItemClick;
            }

            if (currentFieldValue != altFieldValue2)
            {
                menuItem = this.ContextMenuStrip.Items.Add(
                    string.IsNullOrEmpty(altFieldValue2) ?
                    string.Format(copyTo, currentFieldValue, altFieldName2) :
                    string.Format(copyToOverwrite, currentFieldValue, altFieldName2, altFieldValue2));
                menuItem.Tag = new KeyValuePair<string, string>(currentFieldValue, altPropertyName2);
                menuItem.Click += CopyToMenuItemClick;
            }

            if (altFieldValue1 != null
                && currentFieldValue != altFieldValue1)
            {
                menuItem = this.ContextMenuStrip.Items.Add(
                    string.IsNullOrEmpty(altFieldValue1) ?
                    string.Format(replaceWith, altFieldName1, altFieldValue1) :
                    string.Format(replaceWithOverwrite, altFieldName1, altFieldValue1, currentFieldValue));
                menuItem.ToolTipText = "";
                menuItem.Tag = new KeyValuePair<string, string>(altFieldValue1, currentPropertyName);
                menuItem.Click += ReplaceValueMenuItemClick;
            }
            if (altFieldValue2 != null
                && currentFieldValue != altFieldValue2)
            {
                menuItem = this.ContextMenuStrip.Items.Add(
                    string.IsNullOrEmpty(altFieldValue2) ?
                    string.Format(replaceWith, altFieldName2, altFieldValue2) :
                    string.Format(replaceWithOverwrite, altFieldName2, altFieldValue2, currentFieldValue));
                menuItem.Tag = new KeyValuePair<string, string>(altFieldValue2, currentPropertyName);
                menuItem.Click += ReplaceValueMenuItemClick;
            }

            this.ContextMenuStrip.Items.Add("-");
            this.ContextMenuStrip.Items.Add(new ToolStripLabel(string.Format("Overwrite {0} Email with...", currentFieldName)));
            this.GetMergeValues(currentCell.RowIndex, currentCell.ColumnIndex);

            this.ContextMenuStrip.Items.Add("-");
            AddClipboardMenuItems(currentCell);

        }


        void CreateContextMenu(Int32 rowIndex, Int32 columnIndex)
        {

            var currentCell = this._detailGrid.Rows[rowIndex].Cells[columnIndex];
            if (currentCell == null)
                return;

            this.ContextMenuStrip = new ContextMenuStrip();
            this.ContextMenuStrip.Opening += this.ContextMenuStrip_Opening;

            this._detailGrid.ContextMenuStrip = this.ContextMenuStrip;

            var currentContact =
                currentCell.OwningRow.Cells["Name"].Value as string ??
                currentCell.OwningRow.Cells["EmailPrimary"].Value as string ??
                "<Missing>";

            var rowMarkedToDelete = (bool)this._detailGrid.Rows[rowIndex].Cells["Delete"].Value == true;
            if (rowMarkedToDelete)
            {
                var item = this.ContextMenuStrip.Items.Add(string.Format("Unmark '{0}' for Deletion", currentContact), null, UnMarkForDeletionItemClick);
                item.Tag = currentCell;
                return;//don't suggest values if the row is to be deleted
            }



            this.ContextMenuStrip.Items.Add(string.Format("Mark '{0}' for Deletion", currentContact), null, MarkForDeletionItemClick).Tag = currentCell;
            this.ContextMenuStrip.Items.Add("-");

            this.ContextMenuStrip.Items.Add(new ToolStripLabel(string.Format("Rearrange '{0}'...", currentContact)));

            //.Tags are (value to copy, destination) KeyValuePairs
            if (currentCell.OwningColumn.Name == "EmailOther")
            {
                this.AddCellMenuItems("Email", currentCell, "Other", "EmailOther", "Work", "EmailWork", "Home", "EmailHome");
            }
            else if (currentCell.OwningColumn.Name == "EmailHome")
            {
                this.AddCellMenuItems("Email", currentCell, "Home", "EmailHome", "Work", "EmailWork", "Other", "EmailOther");
            }
            else if (currentCell.OwningColumn.Name == "EmailWork")
            {
                this.AddCellMenuItems("Email", currentCell, "Work", "EmailWork", "Other", "EmailOther", "Home", "EmailHome");
            }
            else if (currentCell.OwningColumn.Name == "PhoneOther")
            {
                this.AddCellMenuItems("Phone", currentCell, "Other", "PhoneOther", "Work", "PhoneWork", "Home", "PhoneHome");
            }
            else if (currentCell.OwningColumn.Name == "PhoneHome")
            {
                this.AddCellMenuItems("Phone", currentCell, "Home", "PhoneHome", "Work", "PhoneWork", "Other", "PhoneOther");
            }
            else if (currentCell.OwningColumn.Name == "PhoneWork")
            {
                this.AddCellMenuItems("Phone", currentCell, "Work", "PhoneWork", "Other", "PhoneOther", "Home", "PhoneHome");
            }

        }

        private void AddClipboardMenuItems(DataGridViewCell currentCell)
        {
            const string cut = "Cut";
            const string copy = "Copy";
            const string delete = "Delete";

            string paste;
            if (Clipboard.ContainsText())
            {
                var clipboardText = Clipboard.GetText();
                paste = clipboardText.Length > 30 ?
                    string.Format("Paste ('{0}...')", clipboardText.Substring(0, 30)) :
                    string.Format("Paste ('{0}')", clipboardText);
            }
            else
            {
                paste = "Paste";
            }
            this.ContextMenuStrip.Items.Add(cut, null, CutMenuItemClick).Enabled = !string.IsNullOrEmpty(currentCell.Value as string);
            this.ContextMenuStrip.Items.Add(copy, null, CopyMenuItemClick).Enabled = !string.IsNullOrEmpty(currentCell.Value as string);
            this.ContextMenuStrip.Items.Add(paste, null, PasteMenuItemClick).Enabled = Clipboard.ContainsText();
            this.ContextMenuStrip.Items.Add(delete, null, DeleteMenuItemClick).Enabled = !string.IsNullOrEmpty(currentCell.Value as string);
        }

        void ContextMenuStrip_Opening(object sender, CancelEventArgs e)
        {

        }

        void MarkForDeletionItemClick(object sender, EventArgs e)
        {
            var cell = ((ToolStripItem)sender).Tag as DataGridViewCell;
            cell.OwningRow.Cells["Delete"].Value = true;
        }

        void UnMarkForDeletionItemClick(object sender, EventArgs e)
        {
            var cell = ((ToolStripItem)sender).Tag as DataGridViewCell;
            cell.OwningRow.Cells["Delete"].Value = false;
        }

        void CutMenuItemClick(object sender, EventArgs e)
        {
            var text = this._detailGrid.CurrentCell.Value as string;
            if (text != null)
            {
                Clipboard.SetText(text);
                this._detailGrid.CurrentCell.Value = string.Empty;
            }
        }

        void CopyMenuItemClick(object sender, EventArgs e)
        {
            var text = this._detailGrid.CurrentCell.Value as string;
            if (text != null)
            {
                Clipboard.SetText(text);
            }
        }

        void PasteMenuItemClick(object sender, EventArgs e)
        {
            var text = Clipboard.GetText();
            if (text != null)
            {
                this._detailGrid.CurrentCell.Value = text;
            }
        }

        void DeleteMenuItemClick(object sender, EventArgs e)
        {
           this._detailGrid.CurrentCell.Value = string.Empty;
        }


        void ReplaceValueMenuItemClick(object sender, EventArgs e)
        {
            var menuItem = (ToolStripItem)sender;
            //.Tags are (value to copy, destination) KeyValuePairs
            KeyValuePair<string, string> copyData = (KeyValuePair<string, string>)menuItem.Tag;
            var valueToCopy = copyData.Key;
            var desination = copyData.Value;
            this._detailGrid.CurrentCell.Value = valueToCopy;
        }
        
        void CopyToMenuItemClick(object sender, EventArgs e)
        {
            var menuItem = (ToolStripItem)sender;
            //.Tags are (value to copy, destination) KeyValuePairs
            KeyValuePair<string, string> copyData = (KeyValuePair<string, string>)menuItem.Tag;
            var valueToCopy = copyData.Key;
            var desination = copyData.Value;
            this._detailGrid.CurrentRow.Cells[desination].Value = valueToCopy;
        }

        bool ItemInMenu(ContextMenuStrip strip, string itemText)
        {
            foreach (ToolStripItem item in strip.Items)
            {
                if (item.Text == itemText)
                    return true;
            }
            return false;
        }

        void SuggestedValueClicked(object sender, EventArgs args)
        {
            ToolStripItem item = sender as ToolStripItem;
            this._detailGrid.CurrentCell.Value = item.Tag as String;
        }

        void UpdateDetailGrid()
        {
            foreach (DataGridViewRow row in this._detailGrid.Rows)
            {
                row.Visible = GetModel(row).Merge;
            }
        }

        private void _gridList_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            var row = this._gridList.Rows[e.RowIndex];
            var model = GetModel(row);
            if ((bool)row.Cells["Merge"].Value == false)
            {
                if(this._currentMergeSet.Contains(model))
                    this._currentMergeSet.Remove(model);
            }
            else
            {
                if(!this._currentMergeSet.Contains(model))
                    this._currentMergeSet.Add(model);
            }
            this._detailGrid.DataSource = null;
            this._detailGrid.DataSource = new BindingList<DetailModel>(this._currentMergeSet);
        }

        private void _discardChangesButton_Click(object sender, EventArgs e)
        {
            this._detailGrid.DataSource = null;
            this._currentMergeSet.Clear();
        }

        private void _uploadChangesButton_Click(object sender, EventArgs e)
        {
            var delete = 0;
            var modify = 0;
            foreach (var model in this._currentMergeSet)
            {
                if (model.Delete)
                    delete++;
                else
                    modify++;
            }

            //confirm upload
            if (DialogResult.OK == MessageBox.Show(
                    string.Format("Are you sure you want to delete {0} Google contacts and modify {1} Google contact?", 
                        delete, 
                        modify), 
                    "Confirm Changes", 
                    MessageBoxButtons.OKCancel, 
                    MessageBoxIcon.Warning, 
                    MessageBoxDefaultButton.Button2))
            {
                
                var results = this._merge.UploadChanges(this._currentMergeSet);

                //process upload results
                var builder = new StringBuilder();
                foreach (var result in results)
                {
                    if (result.Value != null)
                    {
                        builder.AppendLine(string.Format("Upload failed for '{0}': '{1}'", result.Key.Name, result.Value.ToString()));
                    }
                }
                var errorText = builder.ToString();
                if (!string.IsNullOrEmpty(errorText))
                {
                    MessageBox.Show(errorText);
                }
                else
                {
                    //success
                    this._currentMergeSet.Clear();
                    this._detailGrid.DataSource = null;
                    this._gridList.DataSource = null;

                    this.DownloadContacts();
                }
            }
       }

        private void _searchTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!string.IsNullOrEmpty(this._searchTextBox.Text))
            {
                //not sure if this is possibe, but
                if (this._gridList.CurrentCell == null)
                    this._gridList.CurrentCell = this._gridList.Rows[0].Cells["Name"];

                var cell = Find(this._searchTextBox.Text, this._gridList.CurrentCell.RowIndex, this._gridList.CurrentCell.ColumnIndex);

                if (cell != null)
                    this._gridList.CurrentCell = cell;
            }
        }

        DataGridViewCell FindCellWithText(string text, Int32 startAtRow, Int32 startAtCell)
        {
            for (Int32 rowIndex = startAtRow; rowIndex < this._gridList.Rows.Count; rowIndex++)
            {
                var row = this._gridList.Rows[rowIndex];
                var foundCell = this.RowContainsText(text, row);
                if(foundCell != null)
                    return foundCell;
            }
            return null;
        }

        DataGridViewCell RowContainsText(string text, DataGridViewRow row)
        {
            var textToSearchFor = text.ToUpperInvariant();
            foreach (DataGridViewCell cell in row.Cells)
            {
                var cellText = cell.Value == null ? string.Empty : cell.Value.ToString();
                if (cell.Visible
                    && cellText.ToUpperInvariant().Contains(textToSearchFor))
                    return cell;
            }
            return null;
        }

        DataGridViewCell Find(string text, Int32 startAtRow, Int32 startAtCell)
        {
            return this.FindCellWithText(text, startAtRow, startAtCell);
        }

        DataGridViewCell FindNext(string text)
        {
            var currentCell = this._gridList.CurrentCell;
            var rowIndex = currentCell.RowIndex;
            if(rowIndex < this._gridList.RowCount - 1)
            {
                //advance one row
                rowIndex++;
            }
            return this.Find(text, rowIndex, this._gridList.Columns["Name"].Index);
        }

        private void _clearSearchButton_Click(object sender, EventArgs e)
        {
            this._gridList.CurrentCell = this._gridList.Rows[0].Cells["Name"];
            this._searchTextBox.Text = string.Empty;
        }

        private void _findNextButton_Click(object sender, EventArgs e)
        {
            if (this._gridList.CurrentCell == null)
                this._gridList.CurrentCell = this._gridList.Rows[0].Cells["Name"];

            var foundCell = this.FindNext(this._searchTextBox.Text);
            if (foundCell != null)
                this._gridList.CurrentCell = foundCell;
        }

        private void _cancelFilterButton_Click(object sender, EventArgs e)
        {
            this._filterTextBox.Text = string.Empty;
            foreach (DataGridViewRow row in this._gridList.Rows)
            {
                row.Visible = true;
            }

        }

        private void _filterTextBox_TextChanged(object sender, EventArgs e)
        {
            //set no cell as current, b/c grid throws ex if current cell is set invisible
            this._gridList.CurrentCell = null;
            var filterText = this._filterTextBox.Text;

            //reset the visibility in case they are making the filter less specific
            foreach (DataGridViewRow row in this._gridList.Rows)
            {
                row.Visible = true;
            }

            //hide rows that dont meet the filter criteria
            foreach (DataGridViewRow row in this._gridList.Rows)
            {
                var foundCell = this.RowContainsText(filterText, row);
                row.Visible = foundCell != null;
            }
        }

        Int32 _splitterDistanceExpanded;
        bool _splitterExpanded = true;
        private void _minimizeGridListButton_Click(object sender, EventArgs e)
        {
            if (this._splitterExpanded)
            {
                this._splitterDistanceExpanded = this._split.SplitterDistance;
                this._split.SplitterDistance = 40;
                this._splitterExpanded = false;
                this._minimizeGridListButton.Text = ">>";
            }
            else
            {
                this._split.SplitterDistance = this._splitterDistanceExpanded;
                this._splitterExpanded = true;
                this._minimizeGridListButton.Text = "<<";
            }
        }

        private void _detailGrid_CellClick(object sender, DataGridViewCellEventArgs e)
        {
           // this.CreateContextMenu(e.RowIndex, e.ColumnIndex);
        }

        private void _detailGrid_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.ColumnIndex == -1
                || e.RowIndex == -1)
                return;
            this.CreateContextMenu(e.RowIndex, e.ColumnIndex);
            this._detailGrid.CurrentCell = this._detailGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];

        }


    }
}
